// overload 1: direct setup function
export function defineComponent<Props, RawBindings = object>(
  setup: (
    props: Readonly<Props>,
    ctx: SetupContext
  ) => RawBindings | RenderFunction
): {
  new (): ComponentPublicInstance<
    Props,
    RawBindings,
    {},
    {},
    {},
    // public props
    VNodeProps & Props
  >
  } & FunctionalComponent<Props>

/**
 * 理解：
 * defineComponent函数名
 * <Props, RawBindings = object> 给函数添加两个类型变量
 * 参数接收一个setup()函数
 * 
*/




// overload 2: object format with no props
// (uses user defined props interface)
// return type is for Vetur and TSX support
export function defineComponent<
  Props = {},
  RawBindings = {},
  D = {},
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
  E extends EmitsOptions = EmitsOptions,
  EE extends string = string
>(
  options: ComponentOptionsWithoutProps<Props,RawBindings,D,C,M,Mixin,Extends,E,EE>
): DefineComponent<Props, RawBindings, D, C, M, Mixin, Extends, E, EE>


// overload 3: object format with array props declaration
// props inferred as { [key in PropNames]?: any }
// return type is for Vetur and TSX support
export function defineComponent<
  PropNames extends string,
  RawBindings,
  D,
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
  E extends EmitsOptions = Record<string, any>,
  EE extends string = string
>(
  options: ComponentOptionsWithArrayProps<
    PropNames,
    RawBindings,...>
): DefineComponent<
  Readonly<{ [key in PropNames]?: any }>,
  RawBindings,...>
    
// overload 4: object format with object props declaration
// see `ExtractPropTypes` in ./componentProps.ts
export function defineComponent<
  // the Readonly constraint allows TS to treat the type of { required: true }
  // as constant instead of boolean.
  PropsOptions extends Readonly<ComponentPropsOptions>,
  RawBindings,
  D,
  C extends ComputedOptions = {},
  M extends MethodOptions = {},
  Mixin extends ComponentOptionsMixin = ComponentOptionsMixin,
  Extends extends ComponentOptionsMixin = ComponentOptionsMixin,
  E extends EmitsOptions = Record<string, any>,
  EE extends string = string
>(
  options: ComponentOptionsWithObjectProps<
    PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>
): DefineComponent<PropsOptions, RawBindings, D, C, M, Mixin, Extends, E, EE>


// implementation, close to no-op
export function defineComponent(options: unknown) {
  return isFunction(options) ? { setup: options } : options
}





/**
 * 对泛型的理解：
 * 下面这个函数的功能是，接收任意类型的参数并将其返回。
 * T 是一个抽象类型，只有在调用的时候才确定它的值,也可称其为类型变量
 * 
 * 
*/
function id<T>(arg: T): T {
  return arg;
}


/**
 * 假如我们定义了一个 Person 类，这个 Person 类有三个属性，并且都是必填的
 * 
*/
enum Sex {
  Man,
  Woman,
  UnKnow,
}

interface Person {
  name: string;
  sex: Sex;
  age: number;
}
/**
 * 突然有一天，公司运营想搞一个促销活动，也需要用到 Person 这个 shape，
 * 但是这三个属性都可以选填，同时要求用户必须填写手机号以便标记用户和接受
 * 短信。一个很笨的方法是重新写一个新的类：
*/
interface MarketPerson1 {
  name?: string;
  sex?: Sex;
  age?: number;
  phone: string;
}

/**
 * 这明显不够优雅。如果 Person 字段很多呢?这种重复代码会异常多，不利于维护
 * 是否可以根据已有类型，生成新的类型呢？当然可以！答案就是可以用两种对
 * 类型的操作：一种是集合操作，另一种是泛型。
*/

// 集合操作，这样没有做到person的三个属性选填
type MarketPerson2 = Person & { phone: string };

// 泛型 Partial<T> 的具体实现，可以将类型看作值，然后对类型进行编程
type Partial<T> = {
  [P in keyof T]?: T[P] 
}
// 可以看成是上面的函数调用，调用的时候传入了具体的类型 Person
type PartialedPerson = Partial<Person>

// PartialedPerson等与下面的代码片段
// interface Person {
//   name?: string;
//   sex?: Sex;
//   age?: number;
// }

// 即可满足需求变更
type MarketPerson3 = PartialedPerson & { phone: string };


/**
 * 泛型的种类：
 * 
 * 函数泛型
 * 接口泛型
 * 类泛型
 * 
*/

// 接口泛型
interface id<T, U> {
  id1: T;
  id2: U;
}


// 泛型约束
function trace1<T>(arg: T): T {
  console.log(arg);
  return arg;
}

// 使用类型约束：extend关键字
interface Sizeable {
  size: number;
}
function trace2<T extends Sizeable>(arg: T): T {
  console.log(arg.size);
  return arg;
}





/**
 * （容易混淆的箭头函数）
 * 第一个(x: number, y: number) => number不同于es6中的箭头函数，再此他是函数类型声明
 * 
*/
let musum: (x: number, y: number) => number = function (x: number, y: number): number { 
  return x + y
}

/**
 * (容易混淆的 interface 内的小括号）
 * (source:string,subString:string) 是函数声明，不能将其看成boolean类型的属性
 * 
*/

interface SearchFunc {
  (source:string,subString:string):boolean
}



/**
 * 参考文章:
 * https://segmentfault.com/a/1190000022993503
 * https://www.tslang.cn/docs/handbook/generics.html
 * 
*/